/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.transas.zorlog.ui;

import java.io.*;
import java.util.Map;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.*;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

/**
 *
 * @author amalikov
 */
public class AppConfig {
	
	public static final String GENERAL = "general";
	public static final String MAIN_WINDOW = "main-window";
	public static final String MAP_WINDOW = "map-window";
	public static final String MAP = "map";
	public static final String FLIGHTGEAR = "flightgear";
	public static final String GCS = "gcs";
	
	public AppConfig(String xmlName) {
		this.xmlName = xmlName;
		options = new TreeMap<>();
		options.put(GENERAL, new TreeMap<String, String>());
		options.put(MAIN_WINDOW, new TreeMap<String, String>());
		options.put(MAP_WINDOW, new TreeMap<String, String>());
		options.put(MAP, new TreeMap<String, String>());
		options.put(FLIGHTGEAR, new TreeMap<String, String>());
		options.put(GCS, new TreeMap<String, String>());
		setDefaults();
	}
	
	public void read() {
		Reader file = null;
		try {
			file = new FileReader(xmlName);
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            DocumentBuilder db = dbf.newDocumentBuilder();
            Document doc = db.parse(new InputSource(file));
			for (String key : options.keySet())
				readOptions(key, doc);
        } catch (FileNotFoundException ex) {
			// do nothing
		} catch (SAXException ex) {
            Logger.getLogger(AppConfig.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(AppConfig.class.getName()).log(Level.SEVERE, null, ex);
        } catch (ParserConfigurationException ex) {
            Logger.getLogger(AppConfig.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
			try {
				if (file != null)
					file.close();
			} catch (IOException ex) {
				Logger.getLogger(AppConfig.class.getName()).log(Level.SEVERE, null, ex);
			}
		}
	}
	
	public void write() {
		try (Writer file = new OutputStreamWriter(new FileOutputStream(xmlName, false), "UTF-8")) {
			StreamResult streamResult = new StreamResult(file);
			TransformerFactory tf = TransformerFactory.newInstance();
			tf.setAttribute("indent-number", 4); 
			Transformer serializer = tf.newTransformer();
			serializer.setOutputProperty(OutputKeys.METHOD, "xml");
			serializer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
			serializer.setOutputProperty(OutputKeys.INDENT, "yes");
			serializer.transform(createDOMSource(), streamResult);
        } catch (TransformerException | IOException | ParserConfigurationException ex) {
			Logger.getLogger(AppConfig.class.getName()).log(Level.SEVERE, null, ex);
        }
	}
	
	private DOMSource createDOMSource() throws ParserConfigurationException {
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		DocumentBuilder db = dbf.newDocumentBuilder();
		DOMImplementation impl = db.getDOMImplementation();
		Document doc = impl.createDocument(null, "config", null);
		doc.setXmlStandalone(true);
		for (String key : options.keySet())
			writeOptions(key, doc);
		return new DOMSource(doc);
	}
	
	public String getOption(String group, String name) {
		return getOptionsGroup(group).get(name);
	}
	
	public void setOption(String group, String name, String value) {
		getOptionsGroup(group).put(name, value);
	}
	
	private Map<String, String> getOptionsGroup(String name) {
		final Map<String, String> optionsGroup = options.get(name);
		return (optionsGroup != null) ? optionsGroup : new TreeMap<String, String>();
	}
	
	private void readOptions(String group, Document doc) {
		final Node node = doc.getDocumentElement().getElementsByTagName(group).item(0);
		final Map<String, String> optionsGroup = getOptionsGroup(group);
		if (node != null) {
			final NodeList children = node.getChildNodes();
			for (int i = 0; i < children.getLength(); ++i) {
				final Node child = children.item(i);
				if ("param".equals(child.getNodeName()))
					optionsGroup.put(child.getAttributes().getNamedItem("name").getNodeValue(), child.getAttributes().getNamedItem("value").getNodeValue());
			}
		}
	}

	private void writeOptions(String group, Document doc) {
		final Element node = doc.createElementNS(null, group);
		final Map<String, String> optionsGroup = getOptionsGroup(group);
		
		for (Map.Entry<String, String> option : optionsGroup.entrySet()) {
			final Element optionNode = doc.createElementNS(null, "param");
			optionNode.setAttribute("name", option.getKey());
			optionNode.setAttribute("value", option.getValue());
			node.appendChild(optionNode);
		}
		
		doc.getDocumentElement().appendChild(node);
	}
	
	private void setDefaults() {
		Map<String, String> o;

		o = getOptionsGroup(GENERAL);
		o.put("format", "FCS Main");
		o.put("lastdir", "");
		
		o = getOptionsGroup(MAIN_WINDOW);
		o.put("x", "0");
		o.put("y", "0");
		o.put("w", "800");
		o.put("h", "600");
		
		o = getOptionsGroup(MAP_WINDOW);
		o.put("x", "0");
		o.put("y", "0");
		o.put("w", "800");
		o.put("h", "600");
		
		o = getOptionsGroup(MAP);
		o.put("latitude", "Latitude");
		o.put("longitude", "Longitude");
		o.put("altitude", "Altitude");
		o.put("roll", "Roll");
		o.put("pitch", "Pitch");
		o.put("heading", "Heading");
		o.put("offsetenable", "0");
		o.put("offset", "0");
		
		o = getOptionsGroup(FLIGHTGEAR);
		o.put("self", "0.0.0.0");
		o.put("dst", "localhost");
		o.put("dstport", "5556");
		o.put("latitude", "Latitude");
		o.put("longitude", "Longitude");
		o.put("altitude", "Altitude");
		o.put("airspeed", "Airspeed");
		o.put("roll", "Roll");
		o.put("pitch", "Pitch");
		o.put("heading", "Heading");
		o.put("aileron", "Aileron");
		o.put("elevator", "Elevator");
		o.put("rudder", "Rudder");
		o.put("throttle", "Throttle");
		o.put("flaps", "Flaps");
		o.put("rpm", "RPM");
		o.put("offsetenable", "0");
		o.put("offset", "0");
		o.put("filterenable", "1");
		o.put("filteralpha", "0.2");
		o.put("baroalt", "BaroAlt");
		o.put("vspeed", "Vspeed");
		o.put("gspeed", "GSpeed");
		o.put("aoa", "AOA");
		o.put("slip", "Slip");

		o = getOptionsGroup(GCS);
		o.put("link", "serial");
		o.put("comport", "COM1");
		o.put("baudrate", "57600");
		o.put("self", "0.0.0.0");
		o.put("selfport", "4001");
		o.put("remote", "localhost");
		o.put("remoteport", "4002");
		o.put("state", "State");
		o.put("roll", "Roll");
		o.put("pitch", "Pitch");
		o.put("heading", "Heading");
		o.put("airspeed", "Airspeed");
		o.put("baroalt", "BaroAlt");
		o.put("vspeed", "Vspeed");
		o.put("course", "Course");
		o.put("gspeed", "GSpeed");
		o.put("latitude", "Latitude");
		o.put("longitude", "Longitude");
		o.put("altitude", "Altitude");
		o.put("rpm", "RPM");
		o.put("temp1", "Temp1");
		o.put("temp2", "Temp2");
		o.put("aileron", "Aileron");
		o.put("elevator", "Elevator");
		o.put("rudder", "Rudder");
		o.put("throttle", "Throttle");
		o.put("flaps", "Flaps");
		o.put("failures", "Failures");
		o.put("voltage", "Voltage");
		o.put("current", "Current");
		o.put("fuel", "Fuel");
	}
	
	private String xmlName;
	private Map<String, Map<String, String>> options;
}
